home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlutil.zip / BACKUPFI.C < prev    next >
C/C++ Source or Header  |  1990-06-07  |  6KB  |  254 lines

  1. /* backupfile.c -- make Emacs style backup file names
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* David MacKenzie <djm@ai.mit.edu>.
  19.    Some algorithms adapted from GNU Emacs. */
  20.  
  21. #include <ctype.h>
  22. #include <sys/types.h>
  23. #include "backupfile.h"
  24. #if defined(USG) || defined(_POSIX_SOURCE)
  25. #define index strchr
  26. #define rindex strrchr
  27. #include <string.h>
  28. #else
  29. #include <strings.h>
  30. #endif
  31.  
  32. #ifdef DIRENT
  33. #include <dirent.h>
  34. #define direct dirent
  35. #define NLENGTH(direct) (strlen((direct)->d_name))
  36. #else
  37. #define NLENGTH(direct) ((direct)->d_namlen)
  38. #ifdef USG
  39. #ifdef SYSNDIR
  40. #include <sys/ndir.h>
  41. #else
  42. #include <ndir.h>
  43. #endif
  44. #else /* must be BSD */
  45. #include <sys/dir.h>
  46. #endif
  47. #endif
  48.  
  49. #ifdef STDC_HEADERS
  50. #include <stdlib.h>
  51. #define ISDIGIT(c) (isdigit ((unsigned char) (c)))
  52. #else
  53. #define ISDIGIT(c) (isascii (c) && isdigit (c))
  54.  
  55. char *malloc ();
  56. #endif
  57.  
  58. /* Which type of backup file names are generated. */
  59. enum backup_type backup_type = none;
  60.  
  61. /* The extension added to file names to produce a simple (as opposed
  62.    to numbered) backup file name. */
  63. char *simple_backup_suffix = "~";
  64.  
  65. static char *basename ();
  66. static char *concat ();
  67. static char *copystring ();
  68. static char *dirname ();
  69. char *find_backup_file_name ();
  70. static char *make_version_name ();
  71. static int max_backup_version ();
  72. static int version_number ();
  73.  
  74. /* Return the name of the new backup file for file FILE,
  75.    allocated with malloc.  Return 0 if out of memory.
  76.    FILE must not end with a '/' unless it is the root directory.
  77.    Do not call this function if backup_type == none. */
  78.  
  79. char *
  80. find_backup_file_name (file)
  81.      char *file;
  82. {
  83.   char *dir;
  84.   char *base_versions;
  85.   int highest_backup;
  86.  
  87.   if (backup_type == simple)
  88.     return concat (file, simple_backup_suffix);
  89.   base_versions = concat (basename (file), ".~");
  90.   if (base_versions == 0)
  91.     return 0;
  92.   dir = dirname (file);
  93.   if (dir == 0)
  94.     {
  95.       free (base_versions);
  96.       return 0;
  97.     }
  98.   highest_backup = max_backup_version (base_versions, dir);
  99.   free (base_versions);
  100.   free (dir);
  101.   if (backup_type == numbered_existing && highest_backup == 0)
  102.     return concat (file, simple_backup_suffix);
  103.   return make_version_name (file, highest_backup + 1);
  104. }
  105.  
  106. /* Return the number of the highest-numbered backup file for file
  107.    FILE in directory DIR.  If there are no numbered backups
  108.    of FILE in DIR, return 0.
  109.    FILE should already have ".~" appended to it. */
  110.  
  111. static int
  112. max_backup_version (file, dir)
  113.      char *file, *dir;
  114. {
  115.   DIR *dirp;
  116.   struct direct *dp;
  117.   int highest_version;
  118.   int this_version;
  119.   int file_name_length;
  120.   
  121.   dirp = opendir (dir);
  122.   if (!dirp)
  123.     return 0;
  124.   
  125.   highest_version = 0;
  126.   file_name_length = strlen (file);
  127.  
  128.   while ((dp = readdir (dirp)) != 0)
  129.     {
  130.       if (dp->d_ino == 0 || NLENGTH (dp) <= file_name_length)
  131.     continue;
  132.       
  133.       this_version = version_number (file, dp->d_name, file_name_length);
  134.       if (this_version > highest_version)
  135.     highest_version = this_version;
  136.     }
  137.   closedir (dirp);
  138.   return highest_version;
  139. }
  140.  
  141. /* Return a string, allocated with malloc, containing
  142.    "FILE.~VERSION~".  Return 0 if out of memory. */
  143.  
  144. static char *
  145. make_version_name (file, version)
  146.      char *file;
  147.      int version;
  148. {
  149.   char *backup_name;
  150.  
  151.   backup_name = malloc (strlen (file) + 16);
  152.   if (backup_name == 0)
  153.     return 0;
  154.   sprintf (backup_name, "%s.~%d~", file, version);
  155.   return backup_name;
  156. }
  157.  
  158. /* If BACKUP is a numbered backup of BASE, return its version number;
  159.    otherwise return 0.  BASE_LENGTH is the length of BASE.
  160.    BASE should already have ".~" appended to it. */
  161.  
  162. static int
  163. version_number (base, backup, base_length)
  164.      char *base;
  165.      char *backup;
  166.      int base_length;
  167. {
  168.   int version;
  169.   char *p;
  170.   
  171.   version = 0;
  172.   if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
  173.     {
  174.       for (p = &backup[base_length]; ISDIGIT (*p); ++p)
  175.     version = version * 10 + *p - '0';
  176.       if (p[0] != '~' || p[1])
  177.     version = 0;
  178.     }
  179.   return version;
  180. }
  181.  
  182. /* Return the leading directories part of PATH,
  183.    allocated with malloc.  If out of memory, return 0. */
  184.  
  185. static char *
  186. dirname (path)
  187.      char *path;
  188. {
  189.   char *newpath;
  190.   char *slash;
  191.   char *scan;
  192.  
  193.   slash = rindex (path, '/');
  194.   if (slash == 0)
  195.     return copystring (".");
  196.  
  197.   newpath = malloc (strlen (path) + 1);
  198.   if (newpath == 0)
  199.     return 0;
  200.   strcpy (newpath, path);
  201.   slash += newpath - path;
  202.   /* Remove any trailing slashes and final element. */
  203.   for (scan = slash; scan > newpath && *scan == '/'; --scan)
  204.     /* Do nothing. */ ;
  205.   if (scan > newpath)
  206.     scan[1] = 0;
  207.   return newpath;
  208. }
  209.  
  210. /* Return NAME with any leading path stripped off.  */
  211.  
  212. static char *
  213. basename (name)
  214.      char *name;
  215. {
  216.   char *base;
  217.  
  218.   base = rindex (name, '/');
  219.   return base ? base + 1 : name;
  220. }
  221.  
  222. /* Return the newly-allocated concatenation of STR1 and STR2.
  223.    If out of memory, return 0. */
  224.  
  225. static char *
  226. concat (str1, str2)
  227.      char *str1, *str2;
  228. {
  229.   char *newstr;
  230.   char str1_length = strlen (str1);
  231.  
  232.   newstr = malloc (str1_length + strlen (str2) + 1);
  233.   if (newstr == 0)
  234.     return 0;
  235.   strcpy (newstr, str1);
  236.   strcpy (newstr + str1_length, str2);
  237.   return newstr;
  238. }
  239.  
  240. /* Return a newly allocated copy of STR. */
  241.  
  242. static char *
  243. copystring (str)
  244.      char *str;
  245. {
  246.   char *newstr;
  247.   
  248.   newstr = malloc (strlen (str) + 1);
  249.   if (newstr == 0)
  250.     return 0;
  251.   strcpy (newstr, str);
  252.   return newstr;
  253. }
  254.